/*
 * $Id: string_compar.c,v 1.9 2006-06-23 14:37:52 bacon Exp $
 */

#if defined(__MSTRING)
#include <xp/bas/mstring.h>
#elif defined(__WSTRING)
#include <xp/bas/mstring.h>
#else
#include <xp/bas/string.h>
#endif

#include <xp/bas/ctype.h>

int xp_strcmp (const xp_char_t* s1, const xp_char_t* s2)
{
#if defined(_MSC_VER) && defined(_WIN32) && !defined(_WIN64) && defined(_M_IX86)
	_asm
	{
		mov ecx, s1                   ; ecx = s1
		mov edx, s2                   ; edx = s2

	next:
	#if defined(XP_CHAR_IS_MCHAR) && (XP_SIZEOF_CHAR==1)
		movsx eax, byte ptr [ecx]     ; eax = *s1
		movsx ebx, byte ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==2)
		movsx eax, word ptr [ecx]     ; eax = *s1
		movsx ebx, word ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==4)
		mov eax, dword ptr [ecx]      ; eax = *s1
		mov ebx, dword ptr [edx]      ; ebx = *s2
	#else
		#error unsupported char/wchar_t size
	#endif
		cmp eax, ebx                  ; if (eax != ebx) goto ret1
		jne ret1

		test eax, eax                 ; if (eax == 0) goto done
		jz done

	#ifdef XP_CHAR_IS_MCHAR
		add ecx, XP_SIZEOF_CHAR       ; s1++
		add edx, XP_SIZEOF_CHAR       ; s2++
	#else
		add ecx, XP_SIZEOF_WCHAR_T    ; s1++
		add edx, XP_SIZEOF_WCHAR_T    ; s2++
	#endif
		jmp next                      ; goto next

	ret1:
		xor ecx, ecx                  ; ecx = (eax > ebx)? 1: 0
		cmp eax, ebx              
		setg cl
		lea eax, dword ptr[ecx+ecx-1] ; eax = (1+1-1=>1,0+0-1=>-1)

	done:
	}

#else
	while (*s1 == *s2) 
	{
		if (*s1 == XP_CHAR('\0')) return 0;
		s1++, s2++;
	}

	return (*s1 > *s2)? 1: -1;
#endif
}

int xp_strcmpn (const xp_char_t* s1, const xp_char_t* s2, xp_size_t n)
{
#if defined(_MSC_VER) && defined(_WIN32) && !defined(_WIN64) && defined(_M_IX86)
	_asm
	{
		mov edi, n                    ; edi = n
		test edi, edi                 ; if (edi == 0) goto ret0
		jz ret0

		mov ecx, s1                   ; ecx = s1
		mov edx, s2                   ; edx = s2

	next:
	#if defined(XP_CHAR_IS_MCHAR) && (XP_SIZEOF_CHAR==1)
		movsx eax, byte ptr [ecx]     ; eax = *s1
		movsx ebx, byte ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==2)
		movsx eax, word ptr [ecx]     ; eax = *s1
		movsx ebx, word ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==4)
		mov eax, dword ptr [ecx]      ; eax = *s1
		mov ebx, dword ptr [edx]      ; ebx = *s2
	#else
		#error unsupported char/wchar_t size
	#endif

		cmp eax, ebx                  ; if (eax != ebx) goto ret1
		jne ret1

		test eax, eax                 ; if (eax == 0) goto done
		jz done

		cmp edi, 1                    ; if (n == 1) goto ret0
		je ret0

	#ifdef XP_CHAR_IS_MCHAR
		add ecx, XP_SIZEOF_CHAR       ; s1++
		add edx, XP_SIZEOF_CHAR       ; s2++
	#else
		add ecx, XP_SIZEOF_WCHAR_T    ; s1++
		add edx, XP_SIZEOF_WCHAR_T    ; s2++
	#endif
		sub edi, 1                    ; n--
		jmp next
			
	ret0:
		xor eax, eax                  ; eax = 0
		jmp done

	ret1:
		xor ecx, ecx                  ; ecx = (eax > ebx)? 1: 0
		cmp eax, ebx              
		setg cl
		lea eax, dword ptr[ecx+ecx-1] ; eax = (1+1-1=>1,0+0-1=>-1)

	done:
	}
#else
	if (n == 0) return 0;

	while (*s1 == *s2)
	{
		if (*s1 == XP_CHAR('\0') || n == 1) return 0;
		s1++, s2++, n--;
	}

	return (*s1 > *s2)? 1: -1;
#endif
}

int xp_strxcmp (const xp_char_t* s1, xp_size_t len, const xp_char_t* s2)
{
	const xp_char_t* end = s1 + len;
	while (s1 < end && *s2 != XP_CHAR('\0') && *s1 == *s2) s1++, s2++;
	if (s1 == end && *s2 == XP_CHAR('\0')) return 0;
	if (*s1 == *s2) return (s1 < end)? 1: -1;
	return (*s1 > *s2)? 1: -1;
}

int xp_strxncmp (
	const xp_char_t* s1, xp_size_t len1, 
	const xp_char_t* s2, xp_size_t len2)
{
	const xp_char_t* end1 = s1 + len1;
	const xp_char_t* end2 = s2 + len2;

	while (s1 < end1 && s2 < end2 && *s1 == *s2) s1++, s2++;
	if (s1 == end1 && s2 == end2) return 0;
	if (*s1 == *s2) return (s1 < end1)? 1: -1;
	return (*s1 > *s2)? 1: -1;
}

int xp_strcasecmp (const xp_char_t* s1, const xp_char_t* s2)
{
	xp_char_t c1, c2;

	c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	while (c1 == c2 && c2 != XP_CHAR('\0')) 
	{
		s1++, s2++;
		c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	}

	if (c1 > c2) return 1;
	else if (c1 < c2) return -1;
	return 0;
}

int xp_strcasecmpn (const xp_char_t* s1, const xp_char_t* s2, xp_size_t n)
{
	xp_char_t c1, c2;
	if (n == 0) return 0;

	do 
	{
		c1 = xp_toupper(*s1);
		c2 = xp_toupper(*s2);

		if (c1 == XP_CHAR('\0') || c1 != c2) break;
		s1++, s2++;
	} 
	while (--n > 0);

	if (c1 > c2) return 1;
	else if (c1 < c2) return -1;
	return 0;
}

int xp_strxcasecmp (const xp_char_t* s1, xp_size_t len, const xp_char_t* s2)
{
	xp_char_t c1, c2;
	const xp_char_t* end = s1 + len;

	c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	while (s1 < end && c2 != XP_CHAR('\0') && c1 == c2) 
	{
		s1++, s2++;
		c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	}
	if (s1 == end && c2 == XP_CHAR('\0')) return 0;
	if (c1 == c2) return (s1 < end)? 1: -1;
	return (c1 > c2)? 1: -1;
}

int xp_strxncasecmp (
	const xp_char_t* s1, xp_size_t len1, 
	const xp_char_t* s2, xp_size_t len2)
{
	xp_char_t c1, c2;
	const xp_char_t* end1 = s1 + len1;
	const xp_char_t* end2 = s2 + len2;

	c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	while (s1 < end1 && s2 < end2 && c1 == c2) 
	{
		s1++, s2++;
		c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	}
	if (s1 == end1 && s2 == end2) return 0;
	if (c1 == c2) return (s1 < end1)? 1: -1;
	return (c1 > c2)? 1: -1;
}

